import { We_getInstance } from '@/we-api/we-env'
import { We_Legends } from '@/we-api/we-layer/enum-legend'
import WeDataProviderWrapper from '@/we-api/we-layer/layer/WeDataProviderWrapper'

export class ChartPrimitive {
  private dirty = false
  private command: Cesium.DrawCommand
  private dataProvider: WeDataProviderWrapper
  private _readyPromise = Cesium.defer()
  private image: any
  private weUniform: any
  display: WeatherEarth.WeDisplay
  private positions: Cesium.Cartographic[]
  constructor(url, positions) {
    this.positions = positions
    const vec2Uniform = {
      stPoint: new Cesium.Cartesian2(0.0, 1.0),
      edPoint: new Cesium.Cartesian2(0.0, 1.0),
    }

    this.weUniform = new WeatherEarth.WeUniform({
      vec2Uniform,
      name: 'u_WeLengthways',
    })

    this.dataProvider = new WeDataProviderWrapper({
      url,
      parser: new WeatherEarth.Swan2Parser(),
    })
    this.display = new WeatherEarth.WeDisplay({
      valueAndColorRamp: We_Legends.DBZ,
      animation: false,
      isCR: true,
      clampFactor: new Cesium.Cartesian4(50, 0.25, 0, 0),
    })

    this.dataProvider.parser
      .requestImage(url)
      .then((image) => {
        this.image = image
        this.dirty = true
        this._readyPromise.resolve(true)
      })
      .catch(() => {
        this._readyPromise.resolve(false)
      })
  }

  get promise() {
    return this._readyPromise.promise
  }

  rebuild(context) {
    if (this.command) {
      return
    }

    const rectangle = Cesium.Rectangle.fromDegrees(
      this.image.west,
      this.image.south,
      this.image.east,
      this.image.north,
    )

    this.display.weUniform.Properties.extent = rectangle

    WeatherEarth.Util.clampLineInRectangle(this.positions, rectangle)

    const [p1, p2] = this.positions

    this.weUniform.Properties.stPoint = [p1.longitude, p1.latitude]
    this.weUniform.Properties.edPoint = [p2.longitude, p2.latitude]

    const { image } = this
    this.display.valueDecoder.setup(image)

    this.display.weTextureUniform.updateData(context, image, null, {})
    this.display.generateDecodeFunc()
    this.display.legend.build(context)

    const source = this.display.getFragmentShaderSource(true, true, true)
    const fragmentShaderSource = `
    ${this.weUniform.getSource()}
     ${source}
      in vec2 v_textureCoordinates;
      void main() {
        vec2 st = v_textureCoordinates;
        bool shouldFlipY = Weget_shouldFlipY() > 0.0;

        vec2 stPoint = Weget_stPoint();
        vec2 edPoint = Weget_edPoint();
        vec4 worldExtent = Weget_extent();
        vec2 stUV = WeGetImageryLocalUV(stPoint,worldExtent);
        vec2 edUV = WeGetImageryLocalUV(edPoint,worldExtent);
        float length = st.x;
        vec3 texcoord = vec3(0.0,0.0,st.y);
        texcoord.xy = stUV + (edUV - stUV) * length;
        texcoord.y = czm_branchFreeTernary(shouldFlipY,1.0 - texcoord.y,texcoord.y);

        float value = WeDecodeValue(texcoord);
        vec4 color = WeSampleColorByValue(value);
        color = czm_branchFreeTernary(WeIsNodata(value),u_TFNoDataColor,color);
        if(color.a == 0.0) color = vec4(vec3(0.5),0.0);
        // if(color.a == 0.0) discard;
        out_FragColor = color;
      }
    `

    const uniformMap = {}
    this.weUniform.setupUniformMap(uniformMap)
    this.display.setupUniformMap(uniformMap)
    const renderState = Cesium.RenderState.fromCache({})

    this.command = new Cesium.DrawCommand({
      vertexArray: context.getViewportQuadVertexArray(),
      shaderProgram: Cesium.ShaderProgram.fromCache({
        context,
        vertexShaderSource: Cesium._shadersViewportQuadVS,
        fragmentShaderSource,
      }),
      renderState,
      uniformMap,
      pass: Cesium.Pass.OPAQUE,
      persists: true,
    })

    this.image = null
  }

  update(frameState) {
    if (!frameState.passes.render) return
    if (this.dirty) {
      this.dirty = false
      this.rebuild(frameState.context)
    }
    if (this.command) {
      this.display.update(frameState)
      frameState.commandList.push(this.command)
    }
  }

  destroy() {
    this.display = this.display?.destroy()
    if (this.command) this.command.shaderProgram.destroy()
    return Cesium.destroyObject(this)
  }
  isDestroyed() {
    return false
  }
}

export const load = (options: {
  url: string
  p1: Cesium.Cartographic
  p2: Cesium.Cartographic
  filterMin?: number
}) => {
  const { url, p1, p2, filterMin } = options
  const WE = We_getInstance(1)
  if (!WE) return
  if (!url) return
  // 参数
  const primitive = new ChartPrimitive(url, [p1, p2])

  WE.primitiveManager.removeAll()
  primitive.promise.then((valid) => {
    if (valid) {
      WE.primitiveManager.add(primitive)
      primitive.display.filterMin = filterMin ?? -999
      if (!WE.viewer.useDefaultRenderLoop) {
        WE.viewer.scene.render()
      }
    }
  })
}
